package com.zlict.admin.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zlict.admin.domain.RdAssetsCostDetail;
import com.zlict.admin.domain.RdFixedAssets;
import com.zlict.admin.mapper.RdAssetsCostDetailMapper;
import com.zlict.admin.mapper.RdFixedAssetsMapper;
import com.zlict.admin.service.RdFixedAssetsService;
import com.zlict.common.constant.AssetsConstants;
import com.zlict.common.exception.ServiceException;
import com.zlict.common.utils.StringUtils;
import com.zlict.system.mapper.SysDictDataMapper;
import lombok.RequiredArgsConstructor;
import org.apache.poi.hssf.usermodel.HSSFDataFormatter;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;

@Service
@RequiredArgsConstructor
public class RdFixedAssetsServiceImpl implements RdFixedAssetsService {
    private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(RdFixedAssetsServiceImpl.class);
    private static final String SUCCESS_CODE = "00000";
    private static final String SUCCESS_MESSAGE = "操作成功";
    private static final String ADD_TYPE = "1"; // 新增
    private static final String UPDATE_TYPE = "2";
    private static final HSSFDataFormatter FORMATTER = new HSSFDataFormatter(); // 提取为静态常量
    private final RdFixedAssetsMapper rdFixedAssetsMapper;
    private final SysDictDataMapper dictMapper;
    private final RdAssetsCostDetailMapper rdAssetsCostDetailMapper;

    /**
     * 查询固定资产列表
     */
    @Override
    public List<RdFixedAssets> getFixedAssetsList(RdFixedAssets rdFixedAssets) {
        return rdFixedAssetsMapper.getFixedAssetsList(rdFixedAssets);
    }

    /**
     * 固定资产新增
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertRdFixedAssets(RdFixedAssets rdFixedAssets) {
        //判断对象是否为空
        if (ObjectUtil.isNull(rdFixedAssets)) {
            throw new ServiceException("新增对象为空");
        }
        if (!checkStringNotEmpty(rdFixedAssets.getDeviceType())) {
            throw new ServiceException("资产类型不能为空");
        }
        if (!checkStringNotEmpty(rdFixedAssets.getBuyType())) {
            throw new ServiceException("购买方式不能为空");
        }
        // 调用校验方法
        String validationResult = validateFixedAssetsDTO(rdFixedAssets);
        if (validationResult != null) {
            throw new ServiceException(validationResult);
        }
        // 验证是否存在这个设备
        RdFixedAssets u = rdFixedAssetsMapper.getFixedAssetsByDeviceNumber(rdFixedAssets);
        if (u != null) {
            throw new ServiceException("编号已存在！");
        }
//        rdFixedAssets.setCreateBy(SecurityUtils.getUserId());
        int i = rdFixedAssetsMapper.insertRdFixedAssets(rdFixedAssets);
        if (i <= 0) {
            throw new ServiceException("新增失败");
        }
        //添加折旧明细
        return addCostDetail(rdFixedAssets, i, ADD_TYPE);
    }

    /**
     * 固定资产修改
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateRdFixedAssets(RdFixedAssets rdFixedAssets) {
        //判断对象是否为空
        if (ObjectUtil.isNull(rdFixedAssets)) {
            throw new ServiceException("修改对象为空");
        }
        if (!checkStringNotEmpty(rdFixedAssets.getDeviceType())) {
            throw new ServiceException("资产类型不能为空");
        }
        if (!checkStringNotEmpty(rdFixedAssets.getBuyType())) {
            throw new ServiceException("购买方式不能为空");
        }
        // 调用校验方法
        String validationResult = validateFixedAssetsDTO(rdFixedAssets);
        if (validationResult != null) {
            throw new ServiceException(validationResult);
        }
        // 验证是否存在这个设备
        RdFixedAssets u = rdFixedAssetsMapper.getFixedAssetsByDeviceNumber(rdFixedAssets);
        if (u != null) {
            if (!rdFixedAssets.getId().equals(u.getId())) {
                throw new ServiceException("编号已存在！");
            }
        }
//        rdFixedAssets.setUpdateBy(SecurityUtils.getUserId());
        rdFixedAssets.setUpdateTime(DateTime.now());
        int i = rdFixedAssetsMapper.updateRdFixedAssets(rdFixedAssets);
        if (i <= 0) {
            throw new ServiceException("修改失败");
        }
        //添加折旧明细
        return addCostDetail(rdFixedAssets, i, UPDATE_TYPE);
    }

    /**
     * 添加折旧明细
     */
    @Transactional(rollbackFor = Exception.class)
    public int addCostDetail(RdFixedAssets rdFixedAssets, int i, String updateType) {
        try {
            int costType = Integer.parseInt(rdFixedAssets.getBuyType());
            int months = Integer.parseInt(rdFixedAssets.getFirstDevalue());
            // 执行核心逻辑并记录上下文信息
            int a = batchCreateDepreciationDetails(
                    rdFixedAssets.getId(),
                    costType,
                    months,
                    rdFixedAssets.getDevalue(),
                    String.valueOf(rdFixedAssets.getBuyTime()),
                    null,
                    updateType
            );
            if (a <= 0) {
                LOGGER.warn("折旧明细创建失败, 资产ID: {}, 购买类型: {}, 时间: {}",
                        rdFixedAssets.getId(), rdFixedAssets.getBuyType(), rdFixedAssets.getBuyTime());
                throw new ServiceException("折旧明细添加失败");
            }
        } catch (NumberFormatException e) {
            LOGGER.error("参数转换异常, 资产ID: {}, 错误详情: {}",
                    rdFixedAssets.getId(), e.getMessage(), e);
            throw new ServiceException("购买类型格式错误");
        } catch (Exception e) {
            // 参数化日志
            LOGGER.error("未知异常发生在折旧明细创建过程中, 资产ID: {}, 错误详情: {}", rdFixedAssets.getId(), e.getMessage(), e);
            throw new ServiceException("系统内部错误：" + e.getMessage());
        }
        return i;
    }

    /**
     * 固定资产仪器设备导入
     */

    @Override
    public Map<String, Object> importRdFixedAssets(List<RdFixedAssets> list, Boolean isUpdateSupport, Long operName) {
        return getMap(list, isUpdateSupport, operName, AssetsConstants.INSTRUMENTS_EQUIPMENT);
    }

    /**
     * 固定资产建筑物导入
     */
    @Override
    public Map<String, Object> importRdFixedAssetsBuilding(List<RdFixedAssets> list, Boolean isUpdateSupport, Long operName) {
        return getMap(list, isUpdateSupport, operName, AssetsConstants.BUILDING);
    }

    public Map<String, Object> getMap(List<RdFixedAssets> list, Boolean isUpdateSupport, Long operName, String assetType) {
        String alertMessage = "仪器设备";
        if (AssetsConstants.BUILDING.equals(assetType)) {
            alertMessage = "建筑物";
        }
        if (StringUtils.isNull(list) || list.isEmpty()) {
            throw new ServiceException("导入固定资产数据不能为空！");
        }
        Map<String, Object> map = new HashMap<>();
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (RdFixedAssets rd : list) {
            try {
                // 验证是否存在这个设备
                rd.setTab(assetType);
                RdFixedAssets u = rdFixedAssetsMapper.getFixedAssetsByDeviceNumber(rd);
                if (StringUtils.isNull(u)) {
                    rd.setTab(assetType);
                    if (dictMapper != null) {
//                        rd.setDeviceType(dictMapper.selectDictValue("rd_device_type", rd.getDeviceType()));
//                        rd.setUseType(dictMapper.selectDictValue("rd_use_type", rd.getUseType()));
//                        rd.setUseModel(dictMapper.selectDictValue("rd_use_model", rd.getUseModel()));
//                        rd.setSameTimeUse(dictMapper.selectDictValue("rd_same_time_use", rd.getSameTimeUse()));
                    }
                    rd.setCreateBy(operName);
                    rdFixedAssetsMapper.insertRdFixedAssets(rd);
                    successNum++;
                    successMsg.append("<br/>").append(successNum).append("、").append(alertMessage).append(rd.getDeviceName()).append(" 导入成功");
                } else if (isUpdateSupport) {
                    if (ObjectUtil.isNotEmpty(u)) {
                        if (!rd.getId().equals(u.getId())) {
                            throw new ServiceException("编号已存在！");
                        }
                        rd.setUpdateBy(operName);
                        rdFixedAssetsMapper.updateRdFixedAssets(rd);
                        successNum++;
                        successMsg.append("<br/>").append(successNum).append("、").append(alertMessage).append(rd.getDeviceName()).append(" 更新成功");
                    }
                } else {
                    failureNum++;
                    failureMsg.append("<br/>").append(failureNum).append("、").append(alertMessage).append(rd.getDeviceName()).append(" 已存在");
                }
            } catch (Exception e) {
                // 使用参数化日志
                LOGGER.error("导入失败：资产ID={}, 错误详情=", rd.getId(), e);
                failureNum++;
                String msg = "<br/>" + failureNum + "、" + alertMessage + rd.getDeviceName() + " 导入失败：";
                failureMsg.append(msg).append(e.getMessage());
                LOGGER.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }

        map.put("successNum", successNum);
        map.put("failureNum", failureNum);
        map.put("failureMsg", failureMsg);
        return map;
    }

    @Override
    public int deleteRdFixedAssetsByIds(Long[] ids) {
        return rdFixedAssetsMapper.batchUpdate(ids);
    }

    @Override
    public RdFixedAssets selectRdAssetsCostDetailById(Long id) {
        if(ObjectUtil.isEmpty(id)){
            throw new RuntimeException("参数错误");
        }
        // 查询固定资产信息
        RdFixedAssets rs = new RdFixedAssets();
        rs.setId(id);
        RdFixedAssets rdFixedAssets = rdFixedAssetsMapper.getFixedAssetsByDeviceNumber(rs);
        if(ObjectUtil.isNotEmpty(rdFixedAssets)){
            //获取资产详情
            RdAssetsCostDetail rdAssetsCostDetail = new RdAssetsCostDetail();
            rdAssetsCostDetail.setAssetsId(id);
            List<RdAssetsCostDetail> list = rdAssetsCostDetailMapper.selectRdAssetsCostDetailList(rdAssetsCostDetail);
            //设置资产详情列表
            if(ObjectUtil.isNotEmpty(list)){
                rdFixedAssets.setCostDetailList(list);
            }
        }
        return rdFixedAssets;
    }

    @Override
    public int batchEnable(Long[] ids) {
        if (ObjectUtil.isEmpty(ids)) {
           throw new RuntimeException("参数错误");
        }
        return rdFixedAssetsMapper.batchEnable(ids);
    }

    @Override
    public int batchDisable(Long[] ids) {
        if (ObjectUtil.isEmpty(ids)) {
            throw new RuntimeException("参数错误");
        }
        return rdFixedAssetsMapper.batchDisable(ids);
    }

    /**
     * 批量生成折旧明细
     */
    @Transactional(rollbackFor = Exception.class) // 补充rollbackFor
    public int batchCreateDepreciationDetails(Long assetsId, Integer costType, Integer months,
                                              BigDecimal monthlyAmount, String startDateStr, Long userId, String addType) {
        if ("2".equals(addType)) {
            //根据资产id删除原有明细
            rdAssetsCostDetailMapper.deleteRdAssetsCostDetailByAssetId(assetsId);
        }
        List<RdAssetsCostDetail> details = new ArrayList<>();

        // 使用 DateTimeFormatter 定义对应的日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);

        // 使用定义好的格式和本地化信息进行解析
       LocalDate startDate =  LocalDate.parse(startDateStr, formatter);


        Date currentTime = new Date();

        for (int i = 0; i < months; i++) {
            LocalDate currentMonth = startDate.plusMonths(i);
            YearMonth yearMonth = YearMonth.from(currentMonth);
            RdAssetsCostDetail detail = new RdAssetsCostDetail();
            detail.setAssetsId(assetsId);
            detail.setCostType(costType);
            detail.setCostTime(java.sql.Date.valueOf(yearMonth.atDay(1)));
            detail.setCostAmount(monthlyAmount);
            detail.setCostStartDay("1");
            detail.setCostEndDay(String.valueOf(yearMonth.lengthOfMonth()));
            detail.setCostRule(String.format("本月1日-本月%d日", yearMonth.lengthOfMonth()));
            detail.setUpdateTime(currentTime);
            detail.setCreateBy(userId);
            detail.setCreateTime(currentTime);
            detail.setUpdateBy(userId);
            details.add(detail);
        }
        // 批量新增明细
        return rdAssetsCostDetailMapper.batchInsertRdAssetsCostDetail(details);
    }

    // 封装校验逻辑的方法
    private String validateFixedAssetsDTO(RdFixedAssets dto) {
        if (dto == null) { // 添加空对象检查
            throw new ServiceException("固定资产数据为空");
        }
        // 基础校验规则构建器
        List<ValidationRule> rules = new ArrayList<>(buildBaseValidationRules(dto));

        // 根据设备类型和购买类型添加特定校验规则
        if ("2".equals(dto.getDeviceType())) { // 建筑物
            addBuildingSpecificRules(dto, rules);
        } else { // 仪器设备
            addEquipmentSpecificRules(dto, rules);
        }

        // 使用 Stream API 查找第一个校验失败的规则，若找到则返回对应的错误信息
        return rules.stream()
                .filter(rule -> !rule.validator.test(rule.getter.get()))
                .findFirst()
                .map(rule -> rule.errorMessage)
                .orElse(null); // 校验通过返回 null
    }

    /**
     * 构建基础的校验规则
     */
    private List<ValidationRule> buildBaseValidationRules(RdFixedAssets dto) {
        return Arrays.asList(
                new ValidationRule(dto::getDeviceNumber, "固定资产编号为空", this::checkStringNotEmpty),
                new ValidationRule(dto::getDeviceName, "固定资产名称为空", this::checkStringNotEmpty),
                new ValidationRule(dto::getDeviceType, "固定资产类型为空", this::checkStringNotEmpty),
                new ValidationRule(dto::getTab, "资产标签为空", this::checkStringNotEmpty)
        );
    }

    /**
     * 添加建筑物特定的校验规则
     */
    private void addBuildingSpecificRules(RdFixedAssets dto, List<ValidationRule> rules) {
        String timeFieldName = "1".equals(dto.getBuyType()) ? "购买时间" : "租入时间";
        String costFieldName = "1".equals(dto.getBuyType()) ? "资产原值" : "租赁总价";
        String monthFieldName = "1".equals(dto.getBuyType()) ? "折旧月份" : "租赁月份";
        int maxMonth = 480;

        rules.addAll(Arrays.asList(
                new ValidationRule(dto::getBuyTime, timeFieldName + "为空", this::checkNotNull),
                new ValidationRule(dto::getCost, costFieldName + "为空", this::checkNotNull),
                new ValidationRule(dto::getFirstDevalue, monthFieldName + "必须在1-" + maxMonth + "之间",
                        "1".equals(dto.getBuyType()) ? this::checkFirstDevalues : this::checkPositiveInteger),
                new ValidationRule(dto::getDevalue, "1".equals(dto.getBuyType()) ? "月折旧额必须大于0" : "月租金必须大于0", this::checkDevalue)
        ));
    }

    /**
     * 添加仪器设备特定的校验规则
     */
    private void addEquipmentSpecificRules(RdFixedAssets dto, List<ValidationRule> rules) {
        // 添加仪器设备共有的特殊字段校验
        rules.add(new ValidationRule(dto::getDeviceModel, "固定资产规格为空", this::checkStringNotEmpty));

        String timeFieldName = "1".equals(dto.getBuyType()) ? "购买时间" : "租入时间";
        String costFieldName = "1".equals(dto.getBuyType()) ? "资产原值" : "租赁总价";
        String monthFieldName = "1".equals(dto.getBuyType()) ? "折旧月份" : "租赁月份";
        int maxMonth = 240;

        rules.addAll(Arrays.asList(
                new ValidationRule(dto::getBuyTime, timeFieldName + "为空", this::checkNotNull),
                new ValidationRule(dto::getCost, costFieldName + "为空", this::checkNotNull),
                new ValidationRule(dto::getFirstDevalue, monthFieldName + "必须在1-" + maxMonth + "之间", this::checkFirstDevalue),
                new ValidationRule(dto::getDevalue, "1".equals(dto.getBuyType()) ? "月折旧额必须大于0" : "月租金必须大于0", this::checkDevalue)
        ));
    }

    // 检查字符串是否不为空
    private boolean checkStringNotEmpty(Object value) {
        return value instanceof String str && StrUtil.isNotBlank(str);
    }

    // 检查对象是否不为 null
    private boolean checkNotNull(Object value) {
        return value != null;
    }

    // 检查折旧月份是否符合要求（仪器设备）
    private boolean checkFirstDevalue(Object value) {
        if (!(value instanceof String str) || StrUtil.isBlank(str)) {
            return false;
        }
        try {
            int firstDevalue = Integer.parseInt(str);
            return firstDevalue >= 1 && firstDevalue <= 240;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    // 检查折旧月份是否符合要求（建筑物）
    private boolean checkFirstDevalues(Object value) {
        if (!(value instanceof String str) || StrUtil.isBlank(str)) {
            return false;
        }
        try {
            int firstDevalue = Integer.parseInt(str);
            return firstDevalue >= 1 && firstDevalue <= 480;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    // 检查月折旧额是否符合要求
    private boolean checkDevalue(Object value) {
        if (value == null) {
            return false;
        }
        try {
            double devalue = Double.parseDouble(String.valueOf(value));
            return devalue > 0;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    // 检查是否为正整数
    private boolean checkPositiveInteger(Object value) {
        if (value == null) return false;
        if (value instanceof Integer) {
            return (Integer) value > 0;
        }
        try {
            return Integer.parseInt(value.toString()) > 0;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    // 定义校验规则的内部类
    private record ValidationRule(Supplier<Object> getter, String errorMessage, Predicate<Object> validator) {
    }

    public static String removeSpaces(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        // 分步骤处理空白字符提升可读性
        String cleaned = input.replaceAll("\\\\u[^0-9A-Fa-f]{0,4}", "");
        // 使用双反斜杠进行正确转义
        cleaned = cleaned.replaceAll("[ \\t\\n\\r\\f\\v\\\u00A0\\\u1680\\\u2000-\\\u200A\\\u2028\\\u2029\\\u202F\\\u205F\\\u3000]+", "");
        return cleaned.trim();
    }
    public static String getCellValue(Cell cell) {
        return FORMATTER.formatCellValue(cell);
    }
}